{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Dynamic Programming is nothing but cached recursion\n",
    "\n",
    "Objective of this notebook: compare the performance of\n",
    "- a **basic (vanilla) recursion**\n",
    "- a and **cached recursion**\n",
    "\n",
    "Using Fibonacci example\n",
    "- `fibo(0)` `=` `fibo(1)` `=` `0`\n",
    "- `fibo(n)` `=` `fibo(n-1)` `+` `fibo(n-2)`\n",
    "- hence `fibo_series` `=` `[` `0` `,` `1` `,` `1` `,` `2` `,` `3` `,` `5` `,` `8` `,` `13` `,` `...` `]`\n",
    "\n",
    "Criteria for comparison:\n",
    "- **Count of recursion calls**\n",
    "- **Execution timing**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "533 µs ± 49 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
     ]
    }
   ],
   "source": [
    "def fibo_vanilla_recursive(i):\n",
    "    if i <= 0:\n",
    "        return 0\n",
    "    elif i == 1:\n",
    "        return 1\n",
    "    else:\n",
    "        return fibo_vanilla_recursive(i-1) + fibo_vanilla_recursive(i-2)\n",
    "\n",
    "%timeit for _ in range(10): fibo_vanilla_recursive(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.89 µs ± 180 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n"
     ]
    }
   ],
   "source": [
    "cache = {0:0, 1:1}\n",
    "def fibo_cached_recursive(i):\n",
    "    if cache.get(i) is not None:\n",
    "        return cache[i]\n",
    "    else:\n",
    "        res = fibo_cached_recursive(i-1) + fibo_cached_recursive(i-2)\n",
    "        cache[i] = res\n",
    "        return res\n",
    "\n",
    "%timeit for _ in range(10): fibo_cached_recursive(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.91 µs ± 20.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n"
     ]
    }
   ],
   "source": [
    "from functools import lru_cache\n",
    "\n",
    "@lru_cache(maxsize=128)\n",
    "def fibo_vanilla_recursive(i):\n",
    "    if i <= 0:\n",
    "        return 0\n",
    "    elif i == 1:\n",
    "        return 1\n",
    "    else:\n",
    "        return fibo_vanilla_recursive(i-1) + fibo_vanilla_recursive(i-2)\n",
    "\n",
    "%timeit for _ in range(10): fibo_vanilla_recursive(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's counter **how many times** `fibo_vanilla_recursive()` is called when computing a series of `fibo_vanilla_recursive(i)` (`i in range(10)`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "seen = []\n",
    "counter = 0\n",
    "\n",
    "def fibo_vanilla_recursive(i):\n",
    "    seen.append(i)\n",
    "    # print(\"  computing fibo({})\".format(i))\n",
    "    global counter\n",
    "    counter += 1\n",
    "    if i <= 0:\n",
    "        return 0\n",
    "    elif i == 1:\n",
    "        return 1\n",
    "    else:\n",
    "        return fibo_vanilla_recursive(i-1) + fibo_vanilla_recursive(i-2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "for i = 0, counter = 1\n",
      "for i = 1, counter = 1\n",
      "for i = 2, counter = 3\n",
      "for i = 3, counter = 5\n",
      "for i = 4, counter = 9\n",
      "for i = 5, counter = 15\n",
      "for i = 6, counter = 25\n",
      "for i = 7, counter = 41\n",
      "for i = 8, counter = 67\n",
      "for i = 9, counter = 109\n",
      "total_counter = [276]\n"
     ]
    }
   ],
   "source": [
    "mega_counter = 0\n",
    "res_recursive = []\n",
    "for i in range(10):\n",
    "    global counter\n",
    "    counter = 0\n",
    "    res_recursive.append(fibo_vanilla_recursive(i))\n",
    "    mega_counter += counter\n",
    "    print(\"for i = {}, counter = {}\".format(i, counter))  # to comment when timing\n",
    "\n",
    "print(\"total_counter = [{}]\".format(mega_counter))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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01T3XfvdtGi/mxbt9/ppzJgx85xzm+TL16SC8d69e/Puu++WuxoNYsSIEQwYMKDc1Wh02u+mp6nuu/a7adF+Ny3a7/WTmU0rJJ+6qYiIiIiIlImCcRFZa8yfP5/LL7+c7bffnrZt22JmVY+pU6eWu3qptthiC8yMcePG1Wk9U6dOrbG/62uXunXR0qVL2XTTTdl6662pqCjvXDdLliyha9eu7LDDDmWvi4jUDwXjIrJWWL58Ofvttx933HEHn376KStWrCh3lfJasGAB06dPp1WrVuy4447lro40kFtuuYUvv/ySa665hhYtavbunD17No888gg//elP2W+//WjXrh1mxu67717Qul9//XWOOeYYunbtSps2bdhmm20YNGgQixYtSs3fvn17rrzySj755BPuueeeOu+biJSfgnERWSs8/vjjfPLJJzWWtW/fnm7dutGtWzeaN29eppple//99wHYaaedaNmyZZ3W1bx586p97datG5tskveeH2kEs2fP5tZbb2WrrbbitNNOq5U+fPhwTjvtNO666y7eeustli9fXvC6//SnPzFgwACee+45WrZsyU477cSXX37J7bffzjnnnMO0aendTS+66CK6dOnCL3/5SxYvXlzyvonI2kHBuIisFd57770ar3/84x+zaNEiZs+ezezZs9lss83KVLNsY8eOBWCPPfao87o222yzqn2dPXs2o0ePrvM6pe6GDh3KsmXLOOOMM1K/EHbo0IHDDjuMwYMH83//93/86le/Kmi9Y8eO5ZJLLqGyspK7776bmTNnMmbMGGbOnMmhhx7K3LlzOfHEE1PLtmvXjpNOOol58+bx6KOP1mn/RKT8FIyLyFph6dKlNV73798fMytTbQoTtYwX2iVB1i2VlZXcd999APzoRz9KzXPWWWfx0ksv8etf/5rjjjuOHj16FLTuG2+8kTVr1nDKKadw8cUXV13rnTt3Zvjw4bRr14533nmHF154IbV81Eqvrioi6z4F4yJSVgMHDsTMGDZsWI3lZ555ZubNjGvWrOGpp57iBz/4AZtvvjlt2rRhgw02YJtttuG0005jxIgRtbaTdYPksGHD2HfffenYsSNmVhVgFyJXy/hf//pXOnXqRIsWLRgyZEjedeW7gXP27Nmp6U8//TQHH3wwHTt2pF27dvTr14+HH3644H1Is9NOO2FmvPHGG4wdO5ZTTjmFHj160KxZM6688koAZs6cyR133MHRRx/Ntttuy4YbbkibNm3o06cPV1xxBQsWLEhd93bbbYeZ8c477zBx4kQuuOACevfuTdu2bdluu+245ZZbWLNmTWbd5s+fzy9+8Qu222472rRpw+abb86VV17JsmXLuOOOOzAzzjzzzFrlKioqeOyxxzjyyCPp1q0brVq1olevXpx77rl88cUXqdt64403mD59OjvvvDNbbbVVCUcy3ZIlS/jnP/8JwIUXXlgrvUuXLhx44IEAPPHEE6nr2GuvvejevTvjxo3jo48+qre6iUjja1LjjIvIum/+/PkMGDCAN954o1ba5MmTmTx5Mo888ginnHIKf/nLX2jdunXmus466yweeOCBkuqxcuVKPvnkE8yM3XbbrWp5RUUFV111FUOGDGGTTTbhpZde4uCDDy5pG7lUVlZyzjnncP/999dYPmbMGE4//XTmzJnDoEGDil7vihUr+PTTTwF46623uPrqq2nXrh19+vShZcuWVfs6ZMgQ7rrrLtq2bUuPHj3YYYcdWLBgAZMmTeK2227jxRdfZPTo0bRp06Zq3cuWLWPSpEk0a9aM999/n0svvZS2bduy9dZbs3r1aj777DN+/vOfU1lZyeDBg2vV7f333+c73/kOs2fPpnXr1uy0007MmTOHW2+9lVGjRtGnTx+g9i8VM2bM4LjjjmP06NE0a9aMLbbYgq5duzJx4kTuu+8+/v73v/Pf//631heg6EvdXnvtVfRxzGXs2LGsWLGCVq1asffee6fm2W233fjXv/7Ff//738z17L333jz77LO89tpruoFYZB2mlnERKauOHTvSrVu3GkEb+L64yZsZV69ezVVXXVUrEG/dunWt/ryPPfYYF1xwQeZ2p02bVhWIt23btuhZ4MaPH09FRQVbb701G264IeBbrw899FCGDBnCXnvtxXvvvdcggTj4ADMKxNu2bVsr/ZprruGbb74per3jx49nzZo1mBlXX301N954I3PnzmX06NFMnz69qh/zgQceyNtvv83ixYuZPHkyo0aNYuLEiUyePJlddtmFCRMmMHz48BrrHjduHJWVlTRr1oxBgwbxu9/9jjlz5jB69GhmzJhR1eo+dOjQWvWaN28eRx99NLNnz+b888/nq6++YsyYMcyYMYMnn3ySN954g0ceeQSoGYwvWrSI7373u4wePZof/vCHTJkyhSlTpjB+/HimT5/O4YcfzuzZs7n88strbfP1118HoF+/fkUfx1w+++wzwA+LmXXj76abbgrAlClTWL16dWqe6EvCyJEj67V+ItK4FIyLSFndeeedzJ49u9bNatHy+M2Mw4YNq2q1BR+EP/LIIyxevJjFixdzww031FjHsGHDct4I2aFDB5555hkWL17MkiVL+Oijj9hiiy0Kqneyv/jIkSPZY489GDlyJOeeey4jR46kV69eBa2rVL169eLtt99m2bJljB49mo022qgqbfny5bz66qtFrzPaL+ccN954I4MHD6ZVq1ZV6VHweNxxx7H33nvX+hK05ZZbcuqppwIwYcKE1HVXVFTwxz/+kfPPP79qqMBmzZpx6aWXAqSOInLZZZcxc+ZMTjjhBO655x46duxYlXb88cfzwx/+sGo4zHgwPmjQICZMmMCpp57Kk08+WeP8du3alYceegiAf/zjH6xatarGNqOx7Xv27Jl9wEowf/58wPcPzxJ9wausrMwc5jCq1+eff16v9RORxqVgXETWGcmW1nPPPZdTTz2Vli1b0rZtW6655hr222+/Gnmy+twC3HDDDRx77LFVAeUOO+xQI6DNJeovvvvuuzNkyBAOOeQQFixYwH333ce9996bs3tMfbnzzjurujn069ePY489tkb65MmTi15nfL+iluo0q1ev5m9/+xuXXnopRx99NAcddBD7778/+++/P3fddRdAjSAeqoPxQw45hNNPP73WOqNgOApEIxMnTuSxxx6jbdu2/P73v0+tT//+/QH/ZSAK1D/99FMeeOABOnTowN133516Q3D37t3p3r07K1euZN68eTXS5syZA+QOmksRfWlIHp+4eFrWcIlRvebOnVuPtRORxqY+4yKyzvjggw9qvD7iiCNq5TniiCN48803M8vEpd3oV6gosBw6dCjTpk1j88035+mnn673Lg1ZmjVrxve+970ay7p27VrjdXKEmkJE+3Xaaadlju0+cuRIBg4cmLdFNnnTY7Tuk08+OTV/dCPi9ttvX2P5448/TmVlJSeccELm+OvRl594q/jjjz/OmjVraNGiBUcffXRmPaMgPNlVKgqak8vrKlpfsiU+Lp6W1g0pvryYsc1FZO2jYFxE1hkLFy6s8TotMEsGpMkykc6dO9OhQ4eS6uGcqwryo/VfdNFFjRaIA3Tr1q3WbJDJllbnXFHrrKysZPz48QCZwet7773HEUccwYoVKzjppJMYOHAgu+22G507d6ZVq1asWrWKjTbaiGXLlrHrrrvWWHfUbSWrH3001nxydJpXXnkFgEMPPTSz7tGIKPGyUTed+fPn1/iClqZjx461WsA33nhjZs2alTkyTKmiX1++/vrrzDzRZD7NmjXLvE6j7i5dunSp1/qJSONSNxURWWd06tSpxuu0n+ejrgVZZSLJrhDFmDhxIkuWLKF79+48++yztGzZkquvvpp///vfJa+zWGldHOo6Lnu0X506dWLbbbdNzXPVVVexYsUKfvGLX/D4449zxBFH0L1796r6vPzyyyxbtozmzZvXCMY//fRTli1bRseOHTOHCcwaKnL69OkAbL755pl1j27qjbeMz5w5E/A3jjrncj7SvrR169YNyB00l2K77bYD/H5l3ZwZfbnYaqutMm/yjOqV/AIqIusWBeMiss6IB3cAL774Yq08yWXJMvUhfvPmQQcdxL333ktFRQUnnHACH374Yb1vr7Hkm8TIOcdrr70GwDnnnJOaJxovfocddqjRvSNa9x577JH5pSErGI+621RUVKSW+/DDD3nrrbdq1T0qV2o3jqge9T2O9x577EHr1q1ZtWoVb7/9dmqecePGAbDvvvtmrieqV9++feu1fiLSuBSMi8g646STTqrxeujQoTz66KNUVFSwfPlybrrpplrdEbKmFK+L+E2O4CcuGjx4MN988w1HHXVUrdb5dUUUMO+5556p6cuXL6/qyxz1p457+umneeqpp1LXkW/dCxcu5PPPP6dFixbsvPPONdKiWS3TRsZZtWoV559/Ps45Nt54YzbbbLOqtKglvdRfLA466CAA3nnnnZLKZ2nfvj3/8z//A6TPoDlv3ryq4QpPOOGEzPVE9RowYEC91k9EGpf6jEuD6j04fSrn+jJolwoGNvA2pv7myAZdvxRu4MCB3HrrrVXjNK9cuZIf/ehHnH322axZs6ZWy+nAgQOrRtmoT2ktyL/61a+YOHEiTz/9NMcccwyvvfZavd/419CiLxlZAXO7du3YYostmDZtGjfddBPDhg2jVatWVFZW8uCDD3LxxRfTpk0bVqxYkRmMp81WGt92skUd/E25H3zwAbfddhuHHnpo1TmdOXMm5557btUXsGSL/oknnsi7777LzTffTK9evTjjjDNo1qy6DWrWrFk8/fTTbLDBBqk38x5++OE0b96cd955h5UrV9brCDnXXnstzz//PI899hj77rsvF110EWbG/PnzOemkk1i2bBn9+/fnyCPTP38WLVrEuHHjaNeuXdVsnSKyblLLuIisM1q2bMmvf/3rWsMXrly5slYgfsopp6S2OtaHZMs4+P7aDz30EP369ePtt9/mzDPPLPoGynLLFzCDDyLBj1TSs2dP+vfvT7du3Tj77LMZPHhw1djXyXVE3S6yAv2sLioAV1xxBd27d2fevHnss88+9OnTh1133ZXevXvz8ccfM3DgQKB2d42f/OQnHH744axcuZKzzjqLzp07s8cee9C3b1969uxJz549ueSSSzLvK+jZsydHHXUUixYt4oUX0r/0z5gxgy5dulQ9LrnkEsCPsR5ffsstt9Qot+eee3LnnXdiZlxyySX06tWLvn370qtXL1555RW6dOnCE088kdml56mnnmL16tWcfPLJdbr/QUTKT8G4iKxTOnfuzH/+8x+eeOIJvv/979OrVy9at25N27ZtqyaceeWVV3j00UcbZKzvr776iq+++op27drVusmxXbt2PPfcc/Tq1Yvhw4dXBa7rgvh+RTcYpjnrrLMYNmwYO+ywA4sWLWLGjBkceOCBjBw5kiuuuIKpU6diZjW+qBSy7lzBeNeuXXnzzTf54Q9/SIcOHZg5cybOOa699lomTJhQNUnQAQccUKNcq1at+Oc//8k999zDAQccgJkxYcIEZs6cySabbMLZZ5/NM888w3e/+93M/b3wwgsBqmb3TFqzZg1ff/111WPJkiWpy5ctW1ar7MUXX8yIESM4+uijWblyJRMmTKBHjx5ceuml3HfffWy55ZaZ9Xr44YcBcs4yKyLrBlvXWm7qol+/fu7dd98tdzUaxIgRI9bKfoON0U3ltvEN29tqbeymsrae78bQVPdd+53uyy+/ZLPNNqN9+/bMmjWLdu3a1XsdvvWtbzF69Gg+/vhjttlmm3pff5pc+z1mzBj69evHkUceyd///vdGqU9j0XXetKzv+21mY5xzece8Vcu4iIiskyorKxk4cCCVlZWcc845DRKIA9x+++1UVFRwww03NMj6i3XttdfSokULbr311nJXRUTqgYJxERFZq/3v//4vM2bMqLFs6tSpHH/88bz00kv07t2ba665psG2v88++/DnP/+ZPn36ZA6v2FiWLl3KXnvtxQMPPFBrplIRWTdpNBUREVlrzZ07l5tvvpmbb76Zbt26sfnmm7Nw4UImTZqEc44+ffrw3HPPZd6EWV/OO++8Bl1/oTbYYAOuu+66cldDROqRWsZFRGSt1apVK6644gp23XVXVq9ezdixY/n666854IADuPPOOxk7dmzOG05FRNZ2ahkXEZG1VseOHRkyZAhDhgwpd1VERBqEWsZFRERERMpEwbiIiIiISJkoGBcRERERKRMF4yIiIiIiZaJgXERERESkTBSMi4iIiIiUiYJxEREREZEyUTAuIiIiIlImCsZFRERERMpEwbiIiIiISJkoGBcRERERKRMF4yIiIiIiZaJgXERERESkTBSMi4iIiIiUiYJxEREREZEyUTAuIiIiIlImCsZFRERERMpEwbiIiIiISJkoGBcRERERKZOyB+Nm1hPFO7wAACAASURBVN7MrjKz8Wa22MzmmdlbZjbQzCyRd28zeznkW2Rm/zKz3ctVdxERERGRumhRzo2bWTPgn8C3gAeBu4F2wMnAA8AOwM9D3n2AEcAXwLVhFRcDr5vZt5xz4xu18iIiIiIidVTWYBzYG9gf+J1z7rJooZn9EfgEOJ8QjAN3AauAA51zX4R8TwIfA7cB327EeouIiIiI1Fm5u6l0CM9fxhc651YB84ClAGa2DdAfeCoKxEO+L4CngMPMrHuj1FhEREREpJ6UOxgfBSwEfmZmx5vZ5ma2nZn9GugLXB/y9Q/P/01Zx9uAhfwiIiIiIusMc86VtwJmBwD3AX1iixcDpzvn/hbyDAJuBb7rnPtnovx3gReA851z96as/zzgPIBu3br1HT58eIPsR7ktWbKE9u3bl7satYz/4psGXX+3tvDV8gbdBLts2rFhN1CCtfV8N4amuu/a76ZF+920aL/XTwcffPAY51y/fPnK3WccYAkwAXgOeAvoDFwEPGZmxzjnXsLf1AmwMqX8ivDcLiWNEKDfC9CvXz83YMCA+qv5WmTEiBGsjfs2cPALDbr+QbtUcNv4hr2Mp546oEHXX4q19Xw3hqa679rvpkX73bRov5u2co+msgs+AL/MOXdPbPnj+AB9qJltDSwLSa1TVtMmPC9LSRMRERERWWuVu8/4Zfhg+qn4QufcMnzXky2A3lTf4LlpyjqiZV+kpImIiIiIrLXKHYxHgXTzlLQWsefR4e99U/LtAzhgTP1WTURERESkYZU7GP8oPA+MLzSzTsAxwAJgsnNuEvAucLyZ9Yzl6wkcD7zqnJvdKDUWEREREakn5b6B83fA6cBvQv/xN/E3cJ4L9AAucs5VhLw/BV7Dz7h5d1h2Cf4LxaBGrbWIiIiISD0oazDunJtmZnvhp7c/FDgJWA68Dwxyzj0Ty/uWmQ0AbgoPh7/583jn3LjGrruIiIiISF2Vu2Uc59xk4IwC8/4XH7SLiIiIiKzzyt1nXERERESkyVIwLiIiIiJSJgrGRURERETKRMG4iIiIiEiZKBgXERERESkTBeMiIiIiImWiYFxEREREpEwUjIuIiIiIlImCcRERERGRMlEwLiIiIiJSJgrGRURERETKRMG4iIiIiEiZKBgXERERESkTBeMiIiIiImWiYFxEREREpEwUjIuIiIiIlImCcRERERGRMlEwLiIiIiJSJgrGRURERETKRMG4iIiIiEiZKBgXERERESkTBeMiIiIiImWiYFxEREREpEwUjIuIiIiIlImCcRERERGRMlEwLiIiIiJSJgrGRURERETKRMG4iIiIiEiZKBgXERERESkTBeMiIiIiImWiYFxEREREpEwUjIuIiIiIlImCcRERERGRMlEwLiIiIiJSJgrGRURERETKRMG4iIiIiEiZKBgXERERESkTBeMiIiIiImWiYFxEREREpEwUjIuIiIiIlImCcRERERGRMlEwLiIiIiJSJgrGRURERETKRMG4iIiIiEiZKBgXERERESkTBeMiIiIiImWiYFxEREREpEwUjIuIiIiIlImCcRERERGRMlEwLiIiIiJSJgrGRURERETKRMG4iIiIiEiZKBgXERERESmTgoNxM2tjZl3NbJPYsqPN7DEze8bMjmmYKoqIiIiIrJ+KaRm/AZgFvARgZt8GngVOBI4BngnLRERERESkAMUE43sBBvwjvD4zPFvs8dP6q5qIiIiIyPqtmGB8W8AB48PrvcPrK4Hnw7K+9Vc1EREREZH1WzHB+MbheZaZtQY2B1Y4524D/hTSOtdn5URERERE1mfFBOOV4bkLsGMoOyksWxOel9dTvURERERE1nstisg7DegD3AosxHdRmRDSeobnefVXNRERERGR9VsxLeMv4G/S3AzYJSz7e3iO+op/UE/1EhERERFZ7xUTjN8IvI4PyCuB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\n",
      "text/plain": [
       "<Figure size 864x504 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "from collections import Counter\n",
    "import numpy as np\n",
    "\n",
    "fig = plt.figure(figsize=(12, 7))\n",
    "ax = fig.add_subplot(111)\n",
    "\n",
    "labels, values = zip(*Counter(seen).items())\n",
    "# print(labels, values)\n",
    "indexes = np.arange(len(labels))\n",
    "width = 0.8\n",
    "plt.bar(indexes, values, width)\n",
    "\n",
    "plt.grid()\n",
    "plt.xticks(fontsize=18)\n",
    "plt.xticks(np.arange(min(seen), max(seen)+1, 1.0))\n",
    "plt.yticks(fontsize=18)\n",
    "plt.xlabel(r'$\\ i$', fontsize=18, fontweight='bold')\n",
    "plt.ylabel(\"Number of calls\", fontsize=18, fontweight='bold')\n",
    "plt.title(\"Number of calls to{} \\nwhen computing all{}\\nfor{} in {}\".format(r'$\\ fibo\\_vanilla\\_recursive(i)$', r'$\\ fibo\\_vanilla\\_recursive(k)$', r'$\\ k$', r'$range(10)$'), fontsize=23,  fontweight='bold', y=0.93)\n",
    "fig.savefig('docs/number_calls_fibo_recursive.svg', format='svg', dpi=1500)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bonus digression\n",
    "\n",
    "The plot shows that `fibo_vanilla_recursive(1)` is called `88` times when computing `[` `fibo_vanilla_recursive(0)` `...` `fibo_vanilla_recursive(9)` `]`\n",
    "\n",
    "Could we have analytically computed that result?\n",
    "\n",
    "Let's call `nb_one(k)` the number of calls to `fibo_vanilla_recursive(1)` when involking `fibo_vanilla_recursive(k)`\n",
    "\n",
    "_Initialisation:_\n",
    "- `nb_one(0)` `=` `0`\n",
    "- `nb_one(1)` `=` `1`\n",
    "\n",
    "_Recursion:_\n",
    "- `nb_one(k+2)` `=` `nb_one(k+1)` `+` `nb_one(k)`\n",
    "\n",
    "Hence:\n",
    "`nb_one(k)` `=` `fibonacci(k)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "88\n"
     ]
    }
   ],
   "source": [
    "res = 0\n",
    "for i in range(10):\n",
    "    res += fibo_cached_recursive(i)\n",
    "print(res)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
